﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using cfg.card;
using cfg.character;
using Godot;
using kemocard.Scenes;
using kemocard.Scripts.Buff;
using kemocard.Scripts.Card;
using kemocard.Scripts.Common;
using kemocard.Scripts.Module.Run;
using kemocard.Scripts.MVC;
using kemocard.Scripts.MVC.Controller;
using kemocard.Scripts.MVC.Model;
using kemocard.Scripts.Pawn;

namespace kemocard.Scripts.Module.Battle;

public class BattleModel(BaseController inController) : BaseModel(inController)
{
    public bool IsInBattle { get; private set; }
    public bool IsBattleReady { get; private set; }
    public string PresetId { get; private set; }
    public List<BattleCharacter> Teammates = [];
    public List<BattleEnemy> Enemies = [];
    public int Floor = 0;
    public BattlePhase BattlePhase { get; private set; } = BattlePhase.None;
    private readonly PriorityQueue<EnemyAction, int> _enemyActionQueue = new();
    public int TurnCount { get; private set; }
    public readonly AnimationTaskQueue AnimationTaskQueue = new();

    private bool _newFloor;

    public readonly BasePawn System = new()
    {
        Name = "未知",
        Attribute = 0,
        Description = "未知"
    };

    public override void Init()
    {
        base.Init();
        IsInBattle = false;
        IsBattleReady = false;
        Floor = 0;
        PresetId = "";
        foreach (var teammate in Teammates)
        {
            teammate.Dispose();
        }

        Teammates = [];
        foreach (var battleEnemy in Enemies)
        {
            battleEnemy.Dispose();
        }

        Enemies = [];
        BattlePhase = BattlePhase.None;
        _enemyActionQueue.Clear();
        _ = AnimationTaskQueue.Clear();
    }

    public async Task OnBattleStart(List<BaseCharacter> team, string presetId)
    {
        Init();
        PresetId = presetId;
        GD.Print("开始战斗");
        IsInBattle = true;
        BattlePhase = BattlePhase.BattleStart;
        foreach (var battleCharacter in from baseCharacter in team
                 where baseCharacter != null
                 select new BattleCharacter(baseCharacter))
        {
            battleCharacter.Init();
            battleCharacter.Cost = 2;
            battleCharacter.DrawCard(5);
            Teammates.Add(battleCharacter);
        }

        Teammates.Sort((characterA, battleCharacterB) => characterA.Role - battleCharacterB.Role);

        // foreach (var battleEnemy in enemies.Select(basePawn => new BattleEnemy(basePawn)))
        // {
        //     Enemies.Add(battleEnemy);
        // }
        StartFloor(1);

        IsBattleReady = true;
        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_StartBattle_Ready);
        // GameCore.ControllerMgr.SendUpdate(ControllerType.Run, CommonEvent.StoryOnBattleStart);
        var runMod = GameCore.ControllerMgr.GetControllerModel<RunModel>(ControllerType.Run);
        var task = runMod?.OnStoryOnBattleStart() ?? Task.CompletedTask;
        await task;

        object data = null;
        foreach (var battleCharacter in Teammates)
        {
            await battleCharacter.ExecuteBuffs(ref data, BuffTag.BattleStart);
        }

        foreach (var basePawn in Enemies)
        {
            await basePawn.ExecuteBuffs(ref data, BuffTag.BattleStart);
        }

        GD.Print("战斗初始化完成，开始战斗前行动");
        foreach (var battleCharacter in Teammates)
        {
            battleCharacter.OnBattleStart();
        }
        _enemyActionQueue.Clear();
        foreach (var battleEnemy in Enemies.Where(e => !e.IsDead))
        {
            battleEnemy.Action(_enemyActionQueue);
        }

        while (_enemyActionQueue.Count > 0)
        {
            var item = _enemyActionQueue.Dequeue();
            item.Action?.Invoke(item);
        }

        GD.Print("战斗前行动结束，开始第一回合");
        _ = OnTurnStart();
    }

    public void OnBattleUseCard(BaseBattleCard card)
    {
        if (BattlePhase != BattlePhase.TurnAction) return;
        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_Render);
    }

    public void OnCancelBattleUseCard(BaseBattleCard card)
    {
        if (BattlePhase != BattlePhase.TurnAction) return;
        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_Render);
    }

    public async Task OnTurnStart()
    {
        GD.Print("开始新的回合");
        TurnCount++;
        BattlePhase = BattlePhase.TurnStart;
        foreach (var character in Teammates.Where(character => !character.IsDead))
        {
            character.DrawCard(character.Draw);
            character.Cost += 1;
            character.CanUseCost = character.Cost;
            character.IsConfirm = false;
            character.DoCoolDown(1);
            character.OnTurnStart(TurnCount);
        }

        foreach (var battleEnemy in Enemies.Where(e => !e.IsDead))
        {
            battleEnemy.UpdateActionId();
        }

        var runMod = GameCore.ControllerMgr.GetControllerModel<RunModel>(ControllerType.Run);
        await (runMod?.OnStoryOnTurnStart() ?? Task.CompletedTask);

        GD.Print("抽卡结束，开始执行 buff");
        object data = null;
        var tasks = new List<Task>();
        foreach (var battleCharacter in Teammates.Where(character => !character.IsDead))
        {
            tasks.Add(battleCharacter.ExecuteBuffs(ref data, BuffTag.TurnStart));
            battleCharacter.UsedCardThisTurn.Clear();
            battleCharacter.CountAllBuff();
        }

        await Task.WhenAll(tasks);

        tasks.Clear();
        foreach (var enemy in Enemies)
        {
            tasks.Add(enemy.ExecuteBuffs(ref data, BuffTag.TurnStart));
            enemy.CountAllBuff();
        }

        await Task.WhenAll(tasks);

        GD.Print("执行buff完毕，开始使用卡牌");
        BattlePhase = BattlePhase.TurnAction;
        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_Render);
    }

    public async Task OnTurnEnd()
    {
        GD.Print("回合结束，开始使用卡牌");
        BattlePhase = BattlePhase.UseCard;
        List<BaseBattleCard> useCardList = [];
        useCardList = Teammates.Where(character => !character.IsDead).Aggregate(useCardList,
            (current, battleCharacter) => current.Concat(battleCharacter.TempUsedCard).ToList());

        if (useCardList.Count > 0)
        {
            useCardList.Sort((x, y) => x.Sort.CompareTo(y.Sort));
            var thisCard = useCardList[0];
            useCardList.RemoveAt(0);
            while (thisCard != null)
            {
                if (thisCard.User == null || thisCard.User.IsDead || !IsInBattle)
                {
                    GD.Print($"卡牌{thisCard.Name}的使用者为空或已死亡或战斗/阶层已结束，跳过使用卡牌");
                    continue;
                }

                GD.Print($"使用卡牌{thisCard.Name}");
                List<object> args = [thisCard.User, thisCard.Id];
                GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_CardUsed, args);
                var task = thisCard.UseCardExpose();
                // 等待卡牌使用完毕,或5秒超时
                await Task.WhenAny(task, Task.Delay(5000));
                thisCard.User.Power += 10;
                thisCard = useCardList.Count > 0 ? useCardList[0] : null;
                if (thisCard != null)
                {
                    useCardList.RemoveAt(0);
                }

                if (!IsInBattle)
                {
                    break;
                }
            }
        }

        foreach (var battleCharacter in Teammates.Where(character => !character.IsDead))
        {
            // battleCharacter.Power += battleCharacter.TempUsedCard.Count * 10;
            battleCharacter.TempUsedCard.Clear();
        }

        if (_newFloor)
        {
            StartFloor(Floor + 1);
            return;
        }

        if (!IsInBattle) return;

        GD.Print("卡牌使用完毕，执行回合结束的buff");
        BattlePhase = BattlePhase.TurnEnd;
        object data = null;
        foreach (var battleCharacter in Teammates.Where(character => !character.IsDead))
        {
            await battleCharacter.ExecuteBuffs(ref data, BuffTag.TurnEnd);
        }

        foreach (var battleEnemy in Enemies)
        {
            await battleEnemy.ExecuteBuffs(ref data, BuffTag.TurnEnd);
        }

        GD.Print("执行buff完毕，开始执行敌人行为");
        BattlePhase = BattlePhase.EnemyAction;
        _enemyActionQueue.Clear();
        foreach (var battleEnemy in Enemies.Where(e => !e.IsDead))
        {
            battleEnemy.Action(_enemyActionQueue);
        }

        while (_enemyActionQueue.Count > 0)
        {
            var item = _enemyActionQueue.Dequeue();
            item.Action?.Invoke(item);
        }

        object d = null;
        foreach (var teammate in Teammates)
        {
            await teammate.ExecuteBuffs(ref d, BuffTag.AfterEnemyAction);
        }

        foreach (var enemy in Enemies)
        {
            await enemy.ExecuteBuffs(ref d, BuffTag.AfterEnemyAction);
        }

        GD.Print("执行敌人行为完毕，开始下一回合");
        _ = OnTurnStart();
    }

    public async Task OnBattleEnd(bool isWin = false)
    {
        await AnimationTaskQueue.WaitForAllTasksAsync();
        IsInBattle = false;
        if (isWin && !_newFloor && HasNextFloor())
        {
            _newFloor = true;
            return;
        }

        try
        {
            if (isWin && !string.IsNullOrWhiteSpace(PresetId))
            {
                var mod = GameCore.ControllerMgr.GetControllerModel<RunModel>(ControllerType.Run);
                var conf = GameCore.Tables.TbBattlePreset.GetOrDefault(PresetId);
                if (conf != null)
                {
                    mod?.AddUnhandledReward(conf.Rewards);
                }

                await (mod?.OnStoryOnBattleWin() ?? Task.CompletedTask);
            }
            else
            {
                // todo 失败处理
            }

            Init();
            var runModel = GameCore.ControllerMgr.GetControllerModel<RunModel>(ControllerType.Run);
            var task = runModel?.OnStoryOnBattleEnd() ?? Task.CompletedTask;
            await task;
            GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_EndBattle, isWin);
        }
        catch (Exception e)
        {
            GD.PrintErr(e);
        }
    }

    public async Task DoDamage(Damage damage)
    {
        if (!IsInBattle) return;
        List<BasePawn> pawns =
            [..Teammates.Where(character => !character.IsDead), ..Enemies.Where(character => !character.IsDead)];
        var targets = damage.Target;
        var user = damage.User == System ? System : pawns.Find(pawn => pawn == damage.User);
        if (user == null || targets is not { Count: > 0 }) return;
        switch (user)
        {
            case BattleCharacter { IsDead: true } or BattleEnemy { IsDead: true }:
                return;
            case BattleCharacter when targets.Count == 1 && targets[0] is BattleEnemy battleEnemy:
            {
                if (!battleEnemy.IsDead) break;
                var target = Enemies.Find(enemy => !enemy.IsDead);
                if (target == null)
                {
                    _ = OnBattleEnd(true);
                    return;
                }

                targets = [target];
                break;
            }
            case BattleEnemy when targets.Count == 1 && targets[0] is BattleCharacter battleCharacter:
            {
                if (!battleCharacter.IsDead) break;
                var target = Teammates.Find(c => !c.IsDead);
                if (target == null)
                {
                    _ = OnBattleEnd();
                    return;
                }

                targets = [target];
                break;
            }
        }

        if (targets.Count <= 0) return;

        GD.Print(
            $"伤害检查通过，当前造成伤害者：{damage.User?.Name ?? "未知"}" +
            $"，当前攻击对象:{targets.Aggregate("", (current, target) => current + $"{target.Name} ,")}" +
            $", 伤害值：{damage.Value}, 属性：{damage.Attribute}");

        await user.OnAttack(ref damage);

        var needCheckBattleEnd = false;
        var r = new Random();
        List<Task> tasks = [];
        foreach (var target in targets)
        {
            var tempValue = StaticUtil.CalculateAttributeRate(damage.Value, damage.Attribute, target);
            var tempDamage = damage with { Value = tempValue };
            object data = tempDamage;
            tempDamage.Value = Math.Max(0, tempValue);
            for (var i = 0; i < tempDamage.Times; i++)
            {
                double randomValue;
                if (!tempDamage.IsCritical || !tempDamage.Tags.Contains(Tag.DCRITICAL))
                {
                    var isCritical = damage.Modifiers.GetValueOrDefault(DamageModifier.CertainlyCrit, 0) == 0;
                    randomValue = r.NextDouble();
                    tempDamage.IsCritical = isCritical || randomValue <= tempDamage.CriticalChance;
                }
                else
                {
                    tempDamage.IsCritical = true;
                    tempDamage.CriticalChance = 1;
                    tempDamage.Tags.Add(Tag.DCRITICAL);
                    randomValue = 1;
                }

                GD.Print(
                    $"造成伤害者:{damage.User?.Name ?? "未知"}，攻击对象:{target.Name}，伤害值：{tempDamage.Value}，属性：{damage.Attribute}，暴击率：{tempDamage.CriticalChance},暴击摇点：{randomValue}");
                await target.ExecuteBuffs(ref data, BuffTag.BeforeAttacked);
                tasks.Add(CreateDamageAsyncAction(tempDamage));
                switch (target)
                {
                    case BattleCharacter character:
                    {
                        StaticUtil.ApplyDamage(character, tempDamage);
                        if (character.CurrentHealth <= 0)
                        {
                            character.Dead();
                            needCheckBattleEnd = true;
                        }

                        break;
                    }
                    case BattleEnemy enemy:
                    {
                        StaticUtil.ApplyDamage(enemy, tempDamage);
                        if (enemy.CurrentHealth <= 0)
                        {
                            enemy.Dead();
                            needCheckBattleEnd = true;
                        }

                        break;
                    }
                }
            }

            await target.OnAttacked(ref tempDamage);
        }

        // GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_Render);
        await Task.WhenAll(tasks);
        if (needCheckBattleEnd)
        {
            CheckBattleEnd();
        }
    }

    public async Task DoHeal(HealStruct heal)
    {
        if (!IsInBattle) return;
        var targets = heal.Target;
        var user = heal.User == System ? System : targets.Find(pawn => pawn == heal.User);
        if (user == null || targets is not { Count: > 0 }) return;
        GD.Print(
            $"治疗检查通过，当前治疗者：{heal.User?.Name ?? "未知"}" +
            $", 当前治疗对象:{targets.Aggregate("", (current, target) => current + $"{target.Name} ,")}" +
            $",治疗量：{heal.Value}");
        await user.OnHeal(ref heal);
        var needCheckBattleEnd = false;
        foreach (var target in targets)
        {
            switch (target)
            {
                case BattleCharacter character:
                    if (!character.IsDead)
                    {
                        character.CurrentHealth += heal.Value;
                        await character.OnHealed(heal);
                        if (character.CurrentHealth <= 0)
                        {
                            needCheckBattleEnd = true;
                        }
                    }

                    break;
                case BattleEnemy enemy:
                    if (!enemy.IsDead)
                    {
                        enemy.CurrentHealth += heal.Value;
                        await enemy.OnHealed(heal);
                        if (enemy.CurrentHealth <= 0)
                        {
                            needCheckBattleEnd = true;
                        }
                    }

                    break;
            }
        }

        // GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_Render);

        await CreateHealAsync(heal);
        if (needCheckBattleEnd)
        {
            CheckBattleEnd();
        }
    }

    private void CheckBattleEnd()
    {
        if (Enemies.All(enemy => enemy.IsDead || enemy.CurrentHealth <= 0))
        {
            _ = OnBattleEnd(true);
        }
        else if (Teammates.All(character => character.IsDead || character.CurrentHealth <= 0))
        {
            _ = OnBattleEnd();
        }
    }

    public bool IsTaunt(int taunt)
    {
        if (Teammates.Count <= 0) return false;
        var aliveTeammate = Teammates.Where(character => !character.IsDead);
        var battleCharacters = aliveTeammate as BattleCharacter[] ?? aliveTeammate.ToArray();
        if (battleCharacters.Length <= 0) return false;
        var maxTaunt = battleCharacters.Select(character => character.Taunt).Max();
        var isSameTaunt = battleCharacters.All(c => c.Taunt == maxTaunt);

        return !isSameTaunt && taunt >= maxTaunt;
    }

    private static Task CreateDamageAsyncAction(Damage damage)
    {
        var view = GameCore.ViewMgr.GetView<BattleView>(ViewType.BattleView);
        if (view == null) return null;
        List<Task> tasks = [];
        foreach (var pawn in damage.Target)
        {
            if (pawn is BattleEnemy enemy)
            {
                var enemyItem = view.GetEnemyItemByEnemy(enemy);
                if (enemyItem != null)
                {
                    var task = enemyItem.PlaySprite(damage.AnimUrl, (int)StaticUtil.GetDamageValue(enemy, damage));
                    tasks.Add(task);
                }
            }
        }

        return Task.WhenAll(tasks);
    }

    private static Task CreateHealAsync(HealStruct heal)
    {
        var view = GameCore.ViewMgr.GetView<BattleView>(ViewType.BattleView);
        if (view == null) return null;
        List<Task> tasks = [];
        // todo 治疗异步动画
        return Task.WhenAll(tasks);
    }

    private void StartFloor(int floor)
    {
        var presetConf = GameCore.Tables.TbBattlePreset.GetOrDefault(PresetId);
        if (presetConf == null || presetConf.Monsterlist.Count < floor) return;
        IsInBattle = true;
        _newFloor = false;
        foreach (var enemy in Enemies)
        {
            GameCore.EventBus.RemoveObjAllEvents(enemy);
            enemy.Dispose();
        }

        Enemies.Clear();
        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_RemoveAllEnemies);
        if (floor > presetConf.Monsterlist.Count || floor > presetConf.MonsterPos.Count) return;
        var enemies = presetConf.Monsterlist[floor - 1];
        var positions = presetConf.MonsterPos[floor - 1];
        for (var index = 0; index < enemies.Count; index++)
        {
            var eid = enemies[index];
            var pawn = new BasePawn();
            pawn.InitFromConfig(eid);
            var battleEnemy = new BattleEnemy(pawn);
            var pos = Vector2.Zero;
            if (index >= positions.Count - 1) pos = StaticUtil.Vec2ToVector2(positions[index]);
            battleEnemy.Position = pos;
            Enemies.Add(battleEnemy);
        }

        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_AddEnemies);
        Floor = floor;
        foreach (var teammate in Teammates.Where(teammate => !teammate.IsDead))
        {
            teammate.OnFloorChanged(floor, HasNextFloor());
        }

        GameCore.EventBus.PostEvent(CommonEvent.BattleEvent_StartFloor);
        if (floor != 1) _ = OnTurnStart();
    }

    private bool HasNextFloor()
    {
        var presetConf = GameCore.Tables.TbBattlePreset.GetOrDefault(PresetId);
        return presetConf != null && presetConf.Monsterlist.Count >= Floor + 1;
    }
}

public record Damage
{
    public int Value;
    public Role Role = Role.NORMAL;
    public int Attribute = 0;
    public HashSet<Tag> Tags = [];
    public List<BasePawn> Target = [];
    public BasePawn User;
    public int Times = 1;
    public readonly Dictionary<DamageModifier, float> Modifiers = new();
    public Action<Damage, BasePawn, float> OnHit;
    public int ChainNum = 1;
    public float CriticalChance;
    public bool IsCritical;
    public float CriticalRate = 1.5f;
    public string AnimUrl = "";

    /// <summary>
    /// 是否是最终伤害，不会触发追伤、技能效果
    /// </summary>
    public bool IsFinal = false;
}

public record HealStruct
{
    public int Value = 0;
    public Role Role = Role.NORMAL;
    public int Attribute = 0;
    public List<BasePawn> Target = [];
    public BasePawn User;
    public int ChainNum = 1;
}

public enum DamageModifier
{
    /** 伤害缩放 */
    DamageScale,

    /** 临时防御 */
    TempDefense,

    /// <summary>
    /// 无视防御
    /// </summary>
    NoGuard,

    /// <summary>
    /// 必定暴击
    /// </summary>
    CertainlyCrit,
}

public enum BattlePhase
{
    None,
    BattleStart,
    TurnStart,
    TurnAction,
    UseCard,
    TurnEnd,
    EnemyAction,
}